from random import randint, random
from abc import ABCMeta, abstractmethod


class WeightedChoice(object):
    
    def __init__(self, d):
        self.total = 0
        self.options = []
        for k, v in d.iteritems():
            self.total += v
            self.options.append((self.total, k))

    def get_random(self):
        r = randint(0, self.total - 1)
        for s, value in self.options:
            if s > r:
                return value
        print 'WTF'
        return None


class Grammar(object):
    
    __metaclass__ = ABCMeta
    
    @abstractmethod
    def generate_name(self):
        pass
    

class RussianGrammar(Grammar):
    
    CONSONANTS = WeightedChoice({'sh':1, 'v':3, 'p':2, 'm':1, 'n':3, 'r':2,
                                 'k':1, 'l':2, 's':2, 'f':1, 'b':2, 'd':1,
                                 'dm':1, 't':1, 'ty':1, 'ly':1, 'tch':3})
    VOWELS = WeightedChoice({'a':3, 'e':2, 'o':6, 'i':2, 'u':1})
    TERMINATORS = WeightedChoice({'v':5, 'n':1, 'nsky':1, 'n':1, 'vsky':1, 'enko':1})
    SYLLABUS = WeightedChoice({2:2, 3:3, 4:1})
    
    def generate_name(self):
        name = ''
        
        syllables = self.SYLLABUS.get_random()
        for _ in xrange(syllables):
            name += self.__generate_syllable()
        name += self.TERMINATORS.get_random()
        return name

    def __generate_syllable(self):
        return self.CONSONANTS.get_random() + self.VOWELS.get_random()


class SpanishGrammar(Grammar):
    
    CONSONANTS = WeightedChoice({'b':14, 'c':47, 'd':59, 'f':7, 'g':5, 'j':14,
                                 'k':1, 'l':50, 'm': 31,
                                 'n':67, 'p':25, 'qu':9, 'r':89, 's':80,
                                 't':46, 'v':9, 'x':2, 'y':9, 'z':5, 'gu':10,
                                 'ch':20})
    FIRST_CONSONANT_BLACKLIST = set()
    VOWELS = WeightedChoice({'a':125, 'e':136, 'i':62, 'o':87, 'u':39,
                             'ar':30, 'er':15, 'ir':7, 'or':15, 'ur':6,
                             'al':10, 'el':5, 'il':2, 'ol':5, 'ul':2,
                             'an':10, 'en':5, 'in':2, 'on':5, 'un':2,
                             'as':10, 'es':5, 'is':2, 'os':5, 'us':2})
    TERMINATORS = WeightedChoice({'ez':18, 'a':7, 'es':5, 'o':7, 'e':2, 'an':1,
                                  'ero':5})
    SYLLABUS = WeightedChoice({2:1, 3:4, 4:1})

    def generate_name(self):
        syllables = self.SYLLABUS.get_random()
        name = ''
        
        last_syllable = self.__generate_first_syllable()
        name += last_syllable
        if syllables > 2:
            for _ in xrange(syllables - 2):
                last_syllable = self.__generate_syllable(last_syllable) 
                name += last_syllable
        name += self.__generate_last_syllable(last_syllable)
        name += self.TERMINATORS.get_random()
        return name

    def __generate_first_syllable(self):
        if random() < 0.3:
            return self.VOWELS.get_random()
        else:
            s = self.__generate_syllable(None)
            while any([s.startswith(c) for c in self.FIRST_CONSONANT_BLACKLIST]):
                s = self.__generate_syllable(None)
            return s
        
    def __generate_syllable(self, prev):
        c = self.CONSONANTS.get_random()
        if prev is not None:
            while c in prev:
                c = self.CONSONANTS.get_random()

        v = self.VOWELS.get_random()
        if prev is not None:    
            while v[0] in prev or v[0] in c:
                v = self.VOWELS.get_random()
        else:
            while v[0] in c:
                v = self.VOWELS.get_random()
        return c + v

    def __generate_last_syllable(self, prev):
        c = self.CONSONANTS.get_random()
        if prev is not None:
            while c in prev:
                c = self.CONSONANTS.get_random()
        return c
                
                
def generate_name(grammar=None):
    if grammar is None:
        grammar = SpanishGrammar()
    name = grammar.generate_name()
    name = name[0].upper() + name[1:]
    print 'Generated %s' % name
    return name

